头条项目缓存实现
以用户信息数据缓存为例
common/cache/user.py
from flask import current_app
from redis.exceptions import RedisError
import json
from sqlalchemy.orm import load_only
from models.user import User
from . import constants
class UserProfileCache(object):
"""
用户资料信息缓存
"""
def __init__(self, user_id):
self.key = 'user:{}:info'.format(user_id)
self.user_id = user_id
def save(self):
"""
查询数据库保存缓存记录
:return:
"""
r = current_app.redis_cluster
# 查询数据库
user = User.query.options(load_only(User.name,
User.profile_photo,
User.introduction,
User.certificate)).filter_by(id=self.user_id).first()
# 判断结果是否存在
# 保存到redis中
if user is None:
try:
r.setex(self.key, constants.USER_NOT_EXISTS_CACHE_TTL, -1)
except RedisError as e:
current_app.logger.error(e)
return None
else:
cache_data = {
'name': user.name,
'photo': user.profile_photo,
'intro': user.introduction,
'certi': user.certificate
}
try:
r.setex(self.key, constants.UserProfileCacheTTL.get_val(), json.dumps(cache_data))
except RedisError as e:
current_app.logger.error(e)
return cache_data
def get(self):
"""
获取用户的缓存数据
:return:
"""
r = current_app.redis_cluster
# 先查询redis
try:
ret = r.get(self.key)
except RedisError as e:
current_app.logger.error(e)
ret = None
if ret is not None:
# 如果存在记录,读取
if ret == b'-1':
# 判断记录值,如果为-1,表示用户不存在
return None
# 如果不为-1,需要json转换,返回
else:
return json.loads(ret)
else:
# 如果记录不存在,
cache_data = self.save()
return cache_data
def clear(self):
"""
清除用户缓存
"""
try:
current_app.redis_cluster.delete(self.key)
except RedisError as e:
current_app.logger.error(e)
def exists(self):
"""
判断用户是否存在
"""
# 查询redis
r = current_app.redis_cluster
try:
ret = r.get(self.key)
except RedisError as e:
current_app.logger.error(e)
ret = None
# 如果缓存记录存在
if ret is not None:
if ret == b'-1':
# 如果缓存记录为-1 ,表示用户不存在
return False
else:
# 如果缓存记录不为-1, 表示用户存在
return True
# 如果缓存记录不存在,查询数据库
else:
cache_data = self.save()
if cache_data is not None:
return True
else:
return False
common/cache/constants.py
class BaseCacheTTL(object):
"""
缓存有效期
为防止缓存雪崩,在设置缓存有效期时采用设置不同有效期的方案
通过增加随机值实现
"""
TTL = 0 # 由子类设置
MAX_DELTA = 10 * 60 # 随机的增量上限
@classmethod
def get_val(cls):
return cls.TTL + random.randrange(0, cls.MAX_DELTA)
class UserProfileCacheTTL(BaseCacheTTL):
"""
用户资料数据缓存时间, 秒
"""
TTL = 30 * 60
接口示例
定义获取当前用户信息的接口
GET /v1_0/user
返回JSON
在toutiao/resources/user/__init__.py
中定义路由
user_api.add_resource(profile.CurrentUserResource, '/v1_0/user', endpoint='CurrentUser')
在toutiao/resources/ user/profile.py 中
class CurrentUserResource(Resource):
"""
用户自己的数据
"""
method_decorators = [login_required]
def get(self):
"""
获取当前用户自己的数据
"""
user_data = cache_user.UserProfileCache(g.user_id).get()
user_data['id'] = g.user_id
return user_data